/*
 *  Created at 2011-01-30 15:45 GMT-03:00
 *  Author: Victor Williams Stafusa da Silva
 *  JSmartUnit
 */
package org.jsmartunit.statement;

import java.lang.reflect.Method;

import org.junit.Test;
import org.junit.internal.AssumptionViolatedException;
import org.junit.runners.model.Statement;

/**
 * @author Victor Williams Stafusa da Silva
 */
public class ExpectsException extends Statement {

    private final Statement next;
    private final Class<? extends Throwable> expected;

    private ExpectsException(Statement next, Class<? extends Throwable> expected) {
        this.next = next;
        this.expected = expected;
    }

    public static Statement withExpectedException(Statement next, Method method) {
        Test annotation = method.getAnnotation(Test.class);
        if (annotation == null || annotation.expected() == NONE) return next;
        return new ExpectsException(next, annotation.expected());
    }

    // The Test.None class is not public. So we need this hack to get it.
    // If it were public, Test.None.class would be fine.
    public static final Class<?> NONE;

    static {
        try {
            NONE = (Class<?>) Test.class.getMethod("expected").getDefaultValue();
        } catch (NoSuchMethodException ex) {
            throw new AssertionError(ex);
        }
    }

    @Override
    public void evaluate() throws Exception {
        boolean complete = false;
        try {
            next.evaluate();
            complete = true;
        } catch (AssumptionViolatedException e) {
            throw e;
        } catch (Throwable e) {
            if (!expected.isAssignableFrom(e.getClass())) {
                String message = "Unexpected exception, expected<"
                    + expected.getName() + "> but was<"
                    + e.getClass().getName() + ">";
                throw new Exception(message, e);
            }
        }
        if (complete) {
            throw new AssertionError("Expected exception: " + expected.getName());
        }
    }
}
